#include "dht11.h"
#include "delay.h"
#include "bitband.h"
#include "led.h"
/* 设置 GPIO PC10 为输入模式 */
static void __set_pc10_input(void)
{
	GPIO_InitTypeDef c_init;
	
	c_init.GPIO_Pin = GPIO_Pin_10;
	c_init.GPIO_Speed = GPIO_Speed_50MHz;
	c_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; 	// PC10 浮空输入
	GPIO_Init(GPIOC, &c_init);
}

/* 设置 GPIO PC10 为输出模式 */
static void __set_pc10_output(void)
{
	GPIO_InitTypeDef c_init;
	
	c_init.GPIO_Pin = GPIO_Pin_10;
	c_init.GPIO_Speed = GPIO_Speed_50MHz;
	c_init.GPIO_Mode = GPIO_Mode_Out_PP;  		// PC10 推挽输出
	GPIO_Init(GPIOC, &c_init);
}

void dht11_init(void)
{
	/* PC10 使能 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
	/* 初始化 GPIO 模式为输出模式（用于主机向 DHT11 从机发送数据） */
	__set_pc10_output();
	
	/* 初始化 delay */
	delay_init();
}

/* 设置 DHT11 电平状态（0/1） */
static void __set_pc10_status(uint8_t status)
{
	if (!(0 == status || status == 1))
		return;
	
	__set_pc10_output();	// 输出模式
	PCOut(10) = status;
}

/* 读取 DHT11 电平状态 */
static uint8_t __read_pc10_status(void)
{	
	__set_pc10_input();		// 输入模式
	return PCIn(10);
}

/* 主机向 DHT11 从机发送起始信号 */
static void __send_start(void)
{
	/* 总线空闲状态为高电平,主机把总线拉低等待 DHT11 响应,
	 * 主机把总线拉低必须大于 18 毫秒,保证 DHT11 能检测到起始信号。
	 */
	
	__set_pc10_status(0);
	delay_ms(20);			// 拉低电平至少 18ms
	__set_pc10_status(1);
}

/* 等待 DHT11 响应信号 */
static void __wait_ack(void)
{
	/* DHT11 接收到主机的开始信号后,等待主机开始信号结束,
	 * 然后 DHT11 发送 80us 低电平响应信号.
	 * DHT11 发送响应信号后,再把总线拉高 80us,准备发送数据
	 */

	int i;
	for (i = 0; i < 8; i++) {
		if (!__read_pc10_status())	// 等待拉低电平
			break;
		delay_us(10);
	}

	// 过了 80us...
	
	for (; ; ) {
		if (__read_pc10_status())	// 等待拉高电平
			break;
	}
}

/* 主机关闭通信 */
static void __send_stop(void)
{
	/* 当最后一 bit 数据传送完毕后，DHT11 会拉低总线 50us,
	 * 随后总线由上拉电阻拉高进入空闲状态。
	 */
	
	__set_pc10_status(1);		  	// 主机拉高电平
}
	
/*
 * 用户 MCU 发送一次开始信号后, DHT11 从低功耗模式转换到高速模式,等待主
 * 机开始信号结束后, DHT11 发送响应信号,送出 40bit 的数据,并触发一次信号采集,
 * 用户可选择读取部分数据.从模式下, DHT11 接收到开始信号触发一次温湿度采集,
 * 如果没有接收到主机发送开始信号, DHT11 不会主动进行温湿度采集.采集数据后
 * 转换到低速模式。
 */
void dht11_read_data(uint8_t *temp, uint8_t *humi)
{
	uint8_t i = 0, j = 0, ret = 0;
	uint8_t buff[5] = {0};
	
	/* 1.主机发送开始信号 */
	__send_start();

	/* 2.主机等待 DHT11 发送响应信号 */
	__wait_ack();

	/* 3.读取响应数据 
	 * 一次完整的数据传输为 40bit ,高位先出。 
	 * 数据格式: 8bit 湿度整数数据 + 8bit 湿度小数数据 + 
	 * 8bit 温度整数数据 + 8bit 温度小数数据 + 8bit 校验和
	 * 每一 bit 数据都以 50us 低电平时隙开始,高电平的长短定了数据位是 0 还是 1.
	 * 26 ~ 28ns --> 0
	 * 70ns 	 --> 1
	 * 当最后一 bit 数据传送完毕后, DHT11 拉低总线 50us ,随后总线由上拉电阻拉高进入空闲状态。
	 */
	for (i = 0; i < 40; i++) {
		while (__read_pc10_status()); 	// 等 DHT11 拉低
		// 过了 50us
		while (!__read_pc10_status()); 	// 等 DHT11 拉高
	
		/* 读取数据 bit，依照 0 或 1 的高电平时间差判断 bit */
		delay_us(50);
		ret <<= 1;
		ret |= __read_pc10_status();
		if ((i + 1) % 8 == 0) {
			buff[j++] = ret;
			ret = 0;					// 清空
		}
	}
		
	/* 4.主机关闭通信 */
	__send_stop();
	
	*humi = buff[0];
	*temp = buff[2];
}
